package main

import (
	"bytes"
	"encoding/binary"
	"fmt"
	"io"
	"net"
	"os"
	"regexp"
	"strconv"
	"strings"
	"time"
	"unsafe"
)

//normal connection
func NormalConn(conn net.Conn, head MarkHead) {
	connlocal := conn
	//defer connlocal.Close();
	//define normal target ip and port
	fmt.Printf("get Head:%v\n", head)

	target := head.dstIP
	targetPort := head.dstPort
	fmt.Printf("IP:PORT：%v\n", target.String()+":"+strconv.Itoa(int(targetPort)))
	buf := make([]byte, 38)
	copy(buf, head.dstIP.To4())
	b_buf := bytes.NewBuffer([]byte{})
	binary.Write(b_buf, binary.BigEndian, head.dstPort)
	copy(buf[4:], b_buf.Bytes())
	copy(buf[6:], []byte(head.token))
	fmt.Printf("buf=%v,len(buf)=%d\n", buf, len(buf))
	//begin to connect server
	fmt.Printf("begin to connect :%s\n", srvargs)
	targetConn, err := net.DialTimeout("tcp", srvargs, 30*time.Second)
	if err != nil {
		fmt.Printf("connect %s fail\n", srvargs)
		return

	} else {
		targetConn.Write(buf)
		fmt.Printf("connect %s success,send buff.Bytes,len=%d\n", target, len(buf))
		go func() {
			_, err = io.Copy(targetConn, connlocal)
			if err != nil {
				fmt.Println("io.Copy 1 failed:", err.Error())
			}
		}()
		go func() {
			_, err = io.Copy(connlocal, targetConn)
			if err != nil {
				fmt.Println("io.Copy 1 failed:", err.Error())
			}
		}()

	}
}
func HandAccept() {

	listen, err := net.Listen("tcp", ":28080")
	if err != nil {
		fmt.Println("listen error:", err)
		return
	}
	fmt.Printf("listen success on :28080\n")
	for {

		conn, err := listen.Accept()
		if err != nil {
			fmt.Println("accept error:", err)
			break
		}

		fmt.Printf("accepts one connect:%s\n", conn.RemoteAddr().String())
		go NormalConn(conn, head)
	}
}

type MarkHead struct {
	dstIP   net.IP
	dstPort uint16
	token   string
}

var srvargs string
var head MarkHead

func main() {

	args := os.Args

	if len(args) <= 1 {
		fmt.Printf("No args,exit(0)\n")
		fmt.Printf("usage:ctlclient [SERVERIP]:[PORT]\n")
		return
	}
	srvargs = args[1]
	go HandAccept()
	for {

		command := make([]byte, 32)  // get the command
		cmdarg := make([]byte, 1024) // get the command args
		fmt.Printf("before scanf,command=%s,cmdargs=%s\n", string(command), string(cmdarg))
		fmt.Scanln(&command, &cmdarg) //get input ip and port
		match, _ := regexp.MatchString("[0-9]{1,3}\\.[0-9]{1,3}\\.[0-9]{1,3}\\.[0-9]{1,3}:[0-9]{1,5}", string(cmdarg))
		if match != true {
			fmt.Printf("args error:%v match%s\n", match, string(cmdarg))
			continue
		}
		fmt.Printf("get command:%s,args:%s\n", command, cmdarg)
		switch string(command) {
		//命令为TO，表示要进行转发，转发地址和端口从cmdargs中获取
		case "TO", "To", "to", "tO":
			{
				fmt.Printf("get command %s\n", command)
				args := strings.Split(string(cmdarg), ":")
				dstPort, _ := strconv.Atoi(args[1])
				head.dstIP = net.ParseIP(args[0])
				head.dstPort = uint16(dstPort)
				head.token = "b60d84d4168a2bf7ced43dcf29e7b18e"
				fmt.Printf("len(head)=%d\n%v:%v\ntoken=%s\n", unsafe.Sizeof(head), head.dstIP, head.dstPort, head.token)
				fmt.Printf("addr head=%X\naddr head.dstIP=%X\nhead.dstPort=%X\nhead.token=%X\n", unsafe.Pointer(&head), unsafe.Pointer(&head.dstIP), unsafe.Pointer(&head.dstPort), unsafe.Pointer(&head.token))
				fmt.Printf("client accepting...\n")

			}
			//结束退出本地命令端
		case "exit", "quit", "bye", "close":
			{
				fmt.Printf("get command %s\n", command)
				return
			}
		case "help", "?", "h":
			{
				fmt.Printf("TO [ip]:[port]\n")

			}
		}

	}

}
